import java.io.IOException;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.util.Random;

public class SkiFreeCanvas extends GameCanvas implements Runnable {
    private boolean isPlay; // Game Loop runs when isPlay is true
    private long delay;     // To give thread consistency
    private int currentX = 0, currentY = 0; // To hold current position of the skier
    private int width;              // To hold screen width private
    private int height;             // To hold screen height
    private int direc = 0;
    private int prevKey = 0;
    private int clock = 0;
    private String time = "";
    
    // Sprites to be used 
    private Sprite skier;
    //private int colRec[][] = {{5,26,23,6},{8,20,20,12},{8,16,14,16},{11,16,11,16},{10,16,14,16},{5,20,20,12},{4,26,23,6}};
    private Sprite logo;
    private Sprite sign1;
    private Sprite startR;
    private Sprite startL;
    private Sprite finishR;
    private Sprite finishL;
    private Sprite[] flags;
    private int[] fchange;
    private Sprite[] obsta;
    private int obstaCoords[][];
    Random gen = new Random();
    private int total = 4000;

    // Layer Manager
    private LayerManager layerMan;

    // Constructor and initialization
    public SkiFreeCanvas() throws IOException {
        super(true);
        width = getWidth();
        height = getHeight();
        delay = 0;

        // Layer manager to layer sprites over each other
        layerMan = new LayerManager();

        // Load skier to sprite
        Image buffer = Image.createImage("/men.png");
        skier = new Sprite (buffer,32,32);
        int[] s = {0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,8,8,8,8};
        skier.setFrameSequence(s);
        skier.defineReferencePixel(16,32);
        skier.setRefPixelPosition(width/2, height/4);
        skier.defineCollisionRectangle(5,17,24,16);

        // Load logo and signs to sprites
        buffer = Image.createImage("/logo.png");
        logo = new Sprite (buffer);
        layerMan.append(logo);
        buffer = Image.createImage("/sign1.png");
        sign1 = new Sprite (buffer);
        layerMan.append(sign1);
        buffer = Image.createImage("/start.png");
        startL = new Sprite (buffer,42,27);
        startL.setFrame(0);
        startR = new Sprite (buffer,42,27);
        startR.setFrame(1);
        layerMan.append(startL);
        layerMan.append(startR);
        startL.defineReferencePixel(2,27);
        startR.defineReferencePixel(40,27);
        buffer = Image.createImage("/finish.png");
        finishL = new Sprite (buffer,50,29);
        finishL.setFrame(0);
        finishR = new Sprite (buffer,50,29);
        finishR.setFrame(1);
        layerMan.append(finishL);
        layerMan.append(finishR);
        finishL.defineReferencePixel(2,29);
        finishR.defineReferencePixel(48,29);

        // Load the slalom flags in an array
        flags = new Sprite [30];
        fchange = new int [30];
        buffer = Image.createImage("/flags.png");
        for(int i = 0; i < 30; i++) {
            flags[i] = new Sprite(buffer,12,24);
            flags[i].setFrame((i%2)*2);
            flags[i].defineReferencePixel(6+(i%2),24);
            layerMan.append(flags[i]);
            fchange[i] = 0;
        }

        // Arrays to hold obstacle sprites and coords in map
        obsta = new Sprite [total];
        obstaCoords = new int [2][total];
        gen.setSeed(System.currentTimeMillis());

        // Bounds of map in pixels
        int a = 6000, b = 3000, c = 24000, d = 2000;

        // Load and place each type of obstacle a certain amount of times on random coords
        buffer = Image.createImage("/tree1.png");
        for(int i = 0; i < 800; i++) {
            obsta[i] = new Sprite(buffer);
            obstaCoords[0][i] = gen.nextInt(a) - b;
            obstaCoords[1][i] = gen.nextInt(c) - d;
            obsta[i].defineCollisionRectangle(12,60,9,4);
            layerMan.append(obsta[i]);
        }
        buffer = Image.createImage("/tree2.png");
        for(int i = 800; i < 2600; i++) {
            obsta[i] = new Sprite(buffer);
            obstaCoords[0][i] = gen.nextInt(a) - b;
            obstaCoords[1][i] = gen.nextInt(c) - d;
            obsta[i].defineCollisionRectangle(11,30,8,2);
            layerMan.append(obsta[i]);
        }
        buffer = Image.createImage("/tree3.png");
        for(int i = 2600; i < 3000; i++) {
            obsta[i] = new Sprite(buffer);
            obstaCoords[0][i] = gen.nextInt(a) - b;
            obstaCoords[1][i] = gen.nextInt(c) - d;
            obsta[i].defineCollisionRectangle(7,25,9,2);
            layerMan.append(obsta[i]);
        }
        buffer = Image.createImage("/rock.png");
        for(int i = 3000; i < 3800; i++) {
            obsta[i] = new Sprite(buffer);
            obstaCoords[0][i] = gen.nextInt(a) - b;
            obstaCoords[1][i] = gen.nextInt(c) - d;
            obsta[i].defineCollisionRectangle(0,6,23,5);
            layerMan.append(obsta[i]);
        }
        buffer = Image.createImage("/stump.png");
        for(int i = 3800; i < total; i++) {
            obsta[i] = new Sprite(buffer);
            obstaCoords[0][i] = gen.nextInt(a) - b;
            obstaCoords[1][i] = gen.nextInt(c) - d;
            obsta[i].defineCollisionRectangle(0,6,16,5);
            layerMan.append(obsta[i]);
        }

        layerMan.append(skier);
    }

    // Automatically start thread for game loop
    public void start() {
        isPlay = true;
        Thread t = new Thread(this);
        t.start();
    }

    public void stop() 
    {
        isPlay = false;
        

    }

    // Main Game Loop
    public void run()
    {
        Graphics g = getGraphics();
        while (isPlay == true) {


            // If skier falls, delay before continuing
            if (direc == 12) {
                for (int i = 0; i < 100; i++) {
                    drawScreen(g);
                    
                    // Increment clock to get 'time'
                    if (clock != 0) {
                        clock++;
                        time = "" + clock/60 + ":" + clock%60;
                    }
                }
                skier.setFrame(13);
                direc = 13;
            }

            // Check for key presses
            input();

            // Increment or decrement current coords according to current direction of skier
            if (direc == 3) {
                currentY -= 6;
            }
            else if (direc == 2){
                currentY -= 4;
                currentX -= 2;
            }
            else if (direc == 4){
                currentY -= 4;
                currentX += 2;
            }
            else if (direc == 1){
                currentY -= 2;
                currentX -= 4;
            }
            else if (direc == 5){
                currentY -= 2;
                currentX += 4;
            }

            // Set positions of signs
            logo.setPosition(currentX + width/4 - 45, currentY + 15);
            sign1.setPosition(currentX + width/2 + 58, currentY + height/2);
            startL.setPosition(currentX + width/2 - 100, currentY + height - 60);
            startR.setPosition(currentX + width/2 + 58, currentY + height - 60);
            finishL.setPosition(currentX + width/2 - 80, currentY + height + 12200);
            finishR.setPosition(currentX + width/2 + 88, currentY + height + 12200);
            
            // Start the clock if skier crosses starting line
            if (skier.getRefPixelY() > startL.getRefPixelY() && skier.getRefPixelY() < (startL.getRefPixelY() + 6) && skier.getRefPixelX() > startL.getRefPixelX() && skier.getRefPixelX() < startR.getRefPixelX()) {
                clock = 1;
            }

            // Stop the clock if skier crosses finishing line
            if (skier.getRefPixelY() > finishL.getRefPixelY() && skier.getRefPixelX() > finishL.getRefPixelX() && skier.getRefPixelX() < finishR.getRefPixelX() && clock != 0) {
                clock = 0;
            }

            // Set positions of flags and check if skier passes them
            for(int i = 0; i < 30; i++) {
                flags[i].setPosition(currentX + width/2 - 20 + (i%2)*40, currentY + height + (i+1)*400);
                if (skier.getRefPixelY() > flags[i].getRefPixelY() && fchange[i] == 0 && clock != 0) {
                    if (skier.getRefPixelX() > flags[i].getRefPixelX())
                        flags[i].nextFrame();
                    else
                        flags[i].prevFrame();
                    fchange[i] = 1;

                    // Add time penalty if flag is passed on wrong side
                    if (flags[i].getFrame() == 1)
                       clock += 300; 
                }
            }

            // Check for collision between skier and all obstacles
            for (int i = 0; i < total; i++) {
                obsta[i].setPosition(currentX + obstaCoords[0][i], currentY + obstaCoords[1][i]);
                if (skier.collidesWith(obsta[i],false)) {
                    obsta[i].defineCollisionRectangle(0,0,0,0);
                    skier.setFrameSequence(null);
                    skier.setFrame(12);
                    layerMan.append(obsta[i]);
                    direc = 12;
                }
            }

            drawScreen(g);

            // Increment clock to get 'time'
            if (clock != 0) {
                clock++;
                time = "" + clock/60 + ":" + clock%60;
            }
        }
    }

    // Method to Handle User Inputs 
    private void input() {
        int keyStates = getKeyStates();
        
        // Left
        if (((keyStates & LEFT_PRESSED) != 0) && (keyStates != prevKey) && (direc < 6)) {
            if (direc == 0) {
                skier.setFrameSequence(null);
                skier.setFrame(0);
            }

            skier.nextFrame();
            direc++;
            //skier.defineCollisionRectangle(colRec[0][direc],colRec[1][direc],colRec[2][direc],colRec[3][direc]);

            if (direc == 6) {
                int[] i = {6,6,6,6,6,6,6,6,6,6,6,6,9,9,9,9,10,10,10,10};
                skier.setFrameSequence(i);
            }
        }
        else if ((keyStates & LEFT_PRESSED) != 0 && (direc == 6)) {
            skier.nextFrame();
            currentX += 2;
        }
        
        // Right
        if (((keyStates & RIGHT_PRESSED) != 0) && (keyStates != prevKey) && (direc > 0) && (direc <= 6)) {
            if (direc == 6) {
                skier.setFrameSequence(null);
                skier.setFrame(6);
            }

            skier.prevFrame();
            direc--;
            //skier.defineCollisionRectangle(colRec[0][direc],colRec[1][direc],colRec[2][direc],colRec[3][direc]);

            if (direc == 0) {
                int[] i = {0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,8,8,8,8};
                skier.setFrameSequence(i);
            }
        }
        else if (((keyStates & RIGHT_PRESSED) != 0) && (direc == 0)) {
            skier.nextFrame();
            currentX -= 2;
        }

        // Up
        if (((keyStates & UP_PRESSED) != 0) && ((direc == 0)||(direc == 6))) {
            skier.nextFrame();
            currentY += 2;
        }

        // Down
        if (((keyStates & DOWN_PRESSED) != 0) && (keyStates != prevKey)){
             skier.setFrameSequence(null);
             skier.setFrame(3);
             direc = 3;
             //skier.defineCollisionRectangle(colRec[0][direc],colRec[1][direc],colRec[2][direc],colRec[3][direc]);
        }

        // No button
        if (keyStates == 0 && (direc == 0 || direc == 6))
            skier.setFrame(0);

        prevKey = keyStates;
    }

    // Method to Display Graphics
    private void drawScreen(Graphics g) {
        g.setColor(0xffffff);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(0x0000ff);

        // display sprites
        layerMan.paint(g,0,0);

        // display time
        g.drawString("Time", 0, 0, 0);
        g.drawString(time, 0, 12, 0);
        flushGraphics();
    }
}
